﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.Windows.Forms;

namespace MathService.Functions.Plotter.GraphComponents
{
    public class StackedBarGraph : Graph
    {
        private readonly AxisLine axisLineXandY;
        private readonly BarCollection barCollection;
        private readonly BasicBar basicBar;
        private readonly Color graphBorderColor = Color.Black;
        private readonly Gridline gridline;
        private Color aboveRangeColor = Color.Salmon;
        private RectangleF aboveRangeRect;
        private float aboveRangeValue = 70f;
        private Color barColor = Color.Lime;
        private int barCount = 4;
        private Orientation barOrientation = Orientation.Vertical;
        private float barWidthToSpacingRatio = 1f;
        private Color belowRangeColor = Color.Thistle;
        private RectangleF belowRangeRect;
        private float belowRangeValue = 30f;
        private int graphMarginBottom = 20;
        private int graphMarginLeft = 50;
        private int graphMarginRight = 20;
        private int graphMarginTop = 20;
        private float maximumValue = 100f;
        private float minimumValue;
        private bool showRangeLines = true;
        private bool showRangeValues;
        private TextAlignment valueAlignment = TextAlignment.Smart;

        public StackedBarGraph()
        {
            InitializeComponent();
            basicBar = new BasicBar();
            basicBar.ShowRangeLines = false;
            basicBar.ShowRangeValues = false;
            basicBar.BarGraduation = Graduation.None;
            gridline = new Gridline(this);
            axisLineXandY = new AxisLine(this);
            barCollection = new BarCollection();
            for(int i = 0; i < barCount; i++)
            {
                Bars.Add(new Bar(i.ToString(CultureInfo.CurrentUICulture), 50f));
            }
            base.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint,
                          true);
            base.UpdateStyles();
            base.GraphArea = new Rectangle(base.ClientRectangle.Left + graphMarginLeft,
                                           base.ClientRectangle.Top + graphMarginTop,
                                           (base.ClientRectangle.Width - graphMarginRight) - graphMarginLeft,
                                           (base.ClientRectangle.Height - graphMarginBottom) - graphMarginTop);
        }

        [Category("Appearance"), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
         Description("The color to display if the value is above normal"), Browsable(true)]
        public Color AboveRangeColor
        {
            get { return aboveRangeColor; }
            set
            {
                aboveRangeColor = value;
                base.RefreshDisplay();
            }
        }

        [Description(
                    "A value beyond which the readings are above normal. The bar can be displayed in a different color if it goes above this range."
                    ), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), Category("Appearance"),
         Browsable(true)]
        public float AboveRangeValue
        {
            get { return aboveRangeValue; }
            set
            {
                aboveRangeValue = value;
                base.RefreshDisplay();
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), Description("Color of the bar"),
         Browsable(true), Category("Appearance")]
        public Color BarColor
        {
            get { return barColor; }
            set
            {
                barColor = value;
                base.RefreshDisplay();
            }
        }

        [Description("Number of bars to display on the stacked bar graph"),
         DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), Category("Appearance"),
         Browsable(true)]
        public int BarCount
        {
            get { return barCount; }
            set
            {
                if(value < 0)
                {
                    throw new ArgumentException("Invalid property value. Bar count cannot be negative.");
                }
                if(barCount < value)
                {
                    int num = value - barCount;
                    for(int i = 0; i < num; i++)
                    {
                        Bars.Add(new Bar(i.ToString(CultureInfo.CurrentUICulture), 50f));
                    }
                }
                else if(barCount > value)
                {
                    int num3 = value - barCount;
                    for(int j = 0; j < num3; j++)
                    {
                        Bars.RemoveAt((barCount - 1) - j);
                    }
                }
                barCount = value;
                base.RefreshDisplay();
            }
        }

        [Description("Bar orientation, whether horizontal or vertical"), Category("Appearance"), Browsable(true),
         DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public Orientation BarOrientation
        {
            get { return barOrientation; }
            set
            {
                barOrientation = value;
                base.RefreshDisplay();
            }
        }

        [Browsable(false)]
        public BarCollection Bars { get { return barCollection; } }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), NotifyParentProperty(true),
         Category("Appearance"),
         Description("A ratio that defines the ratio between the width of each bar to the spacing from its adjacent bar"
                     ), Browsable(true)]
        public float BarWidthToSpacingRatio
        {
            get { return barWidthToSpacingRatio; }
            set
            {
                if(value <= 0f)
                {
                    throw new ArgumentException("Invalid property value. Ratio has to be greater than 0.");
                }
                barWidthToSpacingRatio = value;
                base.RefreshDisplay();
            }
        }

        [Description("The color to display if the value is below normal"), Browsable(true),
         DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), Category("Appearance")]
        public Color BelowRangeColor
        {
            get { return belowRangeColor; }
            set
            {
                belowRangeColor = value;
                base.RefreshDisplay();
            }
        }

        [Description(
                    "A value beyond which the readings are below normal. The bar can be displayed in a different color if it goes below this range"
                    ), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), Category("Appearance"),
         Browsable(true)]
        public float BelowRangeValue
        {
            get { return belowRangeValue; }
            set
            {
                belowRangeValue = value;
                base.RefreshDisplay();
            }
        }

        [Browsable(true), Description("The lower margin width of the graph"),
         DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), Category("Appearance")]
        public int GraphMarginBottom
        {
            get { return graphMarginBottom; }
            set
            {
                if(value < 0)
                {
                    throw new ArgumentException("Invalid property value. Margin cannot be negative.");
                }
                graphMarginBottom = value;
                base.RefreshDisplay();
            }
        }

        [Browsable(true), Category("Appearance"), Description("The left side margin width of the graph"),
         DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public int GraphMarginLeft
        {
            get { return graphMarginLeft; }
            set
            {
                if(value < 0)
                {
                    throw new ArgumentException("Invalid property value. Margin cannot be negative.");
                }
                graphMarginLeft = value;
                base.RefreshDisplay();
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
         Description("The right side margin width of the graph"), Category("Appearance"), Browsable(true)]
        public int GraphMarginRight
        {
            get { return graphMarginRight; }
            set
            {
                if(value < 0)
                {
                    throw new ArgumentException("Invalid property value. Margin cannot be negative.");
                }
                graphMarginRight = value;
                base.RefreshDisplay();
            }
        }

        [Category("Appearance"), Description("The upper margin width of the graph"),
         DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), Browsable(true)]
        public int GraphMarginTop
        {
            get { return graphMarginTop; }
            set
            {
                if(value < 0)
                {
                    throw new ArgumentException("Invalid property value. Margin cannot be negative.");
                }
                graphMarginTop = value;
                base.RefreshDisplay();
            }
        }

        [Browsable(true), Category("Appearance"), Description("Maximum value of the bar"),
         DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public float MaximumValue
        {
            get { return maximumValue; }
            set
            {
                maximumValue = value;
                base.RefreshDisplay();
            }
        }

        [Category("Appearance"), Description("Minimum value of the bar"), Browsable(true),
         DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public float MinimumValue
        {
            get { return minimumValue; }
            set
            {
                minimumValue = value;
                base.RefreshDisplay();
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), Browsable(true),
         Category("Appearance"),
         Description("Flag indicating whether lines for above and below normal limits are to be displayed")]
        public bool ShowRangeLines
        {
            get { return showRangeLines; }
            set
            {
                showRangeLines = value;
                base.RefreshDisplay();
            }
        }

        [Browsable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible),
         Category("Appearance"),
         Description("Flag indicating whether the values of above and below ranges are to be displayed")]
        public bool ShowRangeValues
        {
            get { return showRangeValues; }
            set
            {
                showRangeValues = value;
                base.RefreshDisplay();
            }
        }

        [Description("The alignment of the value's text"), Category("Appearance"), Browsable(true),
         DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public TextAlignment ValueAlignment
        {
            get { return valueAlignment; }
            set
            {
                valueAlignment = value;
                base.RefreshDisplay();
            }
        }

        private void CalculateGraphArea()
        {
            base.GraphArea = new Rectangle(base.ClientRectangle.Left + graphMarginLeft,
                                           base.ClientRectangle.Top + graphMarginTop,
                                           (base.ClientRectangle.Width - graphMarginRight) - graphMarginLeft,
                                           (base.ClientRectangle.Height - graphMarginBottom) - graphMarginTop);
        }

        protected override void Dispose(bool disposing) { base.Dispose(disposing); }

        public override void Draw(Graphics graphics)
        {
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            CalculateGraphArea();
            if(((base.GraphArea.Width != 0) && (base.GraphArea.Height != 0)) && (MinimumValue < MaximumValue))
            {
                graphics.SetClip(base.GraphArea);
                gridline.Draw(graphics);
                DrawRangeLinesAndValues(graphics);
                float num = (BarCount*barWidthToSpacingRatio) + (BarCount + 1);
                float barSpacing = 0f;
                if(barOrientation == Orientation.Vertical)
                {
                    barSpacing = (base.GraphArea.Width)/num;
                }
                else
                {
                    barSpacing = (base.GraphArea.Height)/num;
                }
                float num3 = barSpacing*barWidthToSpacingRatio;
                float barArea = num3 + barSpacing;
                float num5 = 0f;
                if(barOrientation == Orientation.Vertical)
                {
                    num5 = base.GraphArea.Left + barSpacing;
                }
                else
                {
                    num5 = (base.GraphArea.Bottom - barSpacing) - num3;
                }
                basicBar.MaximumValue = minimumValue;
                basicBar.MaximumValue = maximumValue;
                for(int i = 0; i < barCount; i++)
                {
                    float barValue = barCollection[i].BarValue;
                    Color barColor = this.barColor;
                    if(barValue > aboveRangeValue)
                    {
                        barColor = aboveRangeColor;
                    }
                    else if(barValue < belowRangeValue)
                    {
                        barColor = belowRangeColor;
                    }
                    barColor = Color.FromArgb(base.Transparency, barColor);
                    if(barOrientation == Orientation.Vertical)
                    {
                        basicBar.ClientRectangle = new Rectangle((int)num5, base.GraphArea.Top, (int)num3,
                                                                 base.GraphArea.Height);
                    }
                    else
                    {
                        basicBar.ClientRectangle = new Rectangle(base.GraphArea.Left, (int)num5, base.GraphArea.Width,
                                                                 (int)num3);
                    }
                    basicBar.BarColor = barColor;
                    basicBar.BarValue = barValue;
                    basicBar.BarOrientation = barOrientation;
                    basicBar.ForeColor = ForeColor;
                    basicBar.ValueFormat = base.ValueFormat;
                    basicBar.ValueAlignment = valueAlignment;
                    basicBar.TextFont = Font;
                    basicBar.BorderColor = graphBorderColor;
                    basicBar.OutOfRangeArrowColor = BarColor;
                    basicBar.Draw(graphics);
                    if(barOrientation == Orientation.Vertical)
                    {
                        num5 += barArea;
                    }
                    else
                    {
                        num5 -= barArea;
                    }
                }
                axisLineXandY.Draw(graphics);
                DrawBarNames(graphics, barArea, barSpacing);
                DrawBarAxisValues(graphics);
            }
        }

        private void DrawBarAxisValues(Graphics graphics)
        {
            graphics.SetClip(base.ClientRectangle);
            if(barOrientation == Orientation.Vertical)
            {
                var format = new StringFormat
                                 {
                                             Trimming = StringTrimming.Character,
                                             FormatFlags = StringFormatFlags.NoWrap,
                                             Alignment = StringAlignment.Far,
                                             LineAlignment = StringAlignment.Center
                                 };
                Brush brush = new SolidBrush(YAxisColor);
                float y = base.GraphArea.Top - (Font.Height/2);
                float num2 = base.GraphArea.Height/base.GraduationsY;
                float num3 = 0f;
                float num4 = (maximumValue - minimumValue)/(base.GraduationsY);
                for(int i = base.GraduationsY; i >= 0; i--)
                {
                    var layoutRectangle = new RectangleF(base.ClientRectangle.Left, y,
                                                         (GraphMarginLeft - (Font.Height/2)), Font.Height);
                    float num6 = maximumValue - (num4*num3);
                    num3++;
                    if(layoutRectangle.IntersectsWith(aboveRangeRect) || layoutRectangle.IntersectsWith(belowRangeRect))
                    {
                        y += num2;
                    }
                    else
                    {
                        string s = "";
                        if(base.ValueFormat.Length != 0)
                        {
                            s = string.Format(CultureInfo.CurrentUICulture, base.ValueFormat, new object[] {num6});
                        }
                        else
                        {
                            s = num6.ToString(CultureInfo.CurrentUICulture);
                        }
                        graphics.DrawString(s, Font, brush, layoutRectangle, format);
                        y += num2;
                    }
                }
            }
            else
            {
                var format2 = new StringFormat
                                  {
                                              Trimming = StringTrimming.Character,
                                              FormatFlags = StringFormatFlags.NoWrap,
                                              Alignment = StringAlignment.Center,
                                              LineAlignment = StringAlignment.Center
                                  };
                Brush brush2 = new SolidBrush(XAxisColor);
                float left = base.GraphArea.Left;
                float num8 = base.GraphArea.Width/base.GraduationsX;
                float num9 = (maximumValue - minimumValue)/(base.GraduationsX);
                for(int j = 0; j <= base.GraduationsX; j++)
                {
                    float num11 = minimumValue + (num9*j);
                    string text = "";
                    if(base.ValueFormat.Length != 0)
                    {
                        text = string.Format(CultureInfo.CurrentUICulture, base.ValueFormat, new object[] {num11});
                    }
                    else
                    {
                        text = num11.ToString(CultureInfo.CurrentUICulture);
                    }
                    SizeF ef2 = graphics.MeasureString(text, Font, (int)num8);
                    var ef3 = new RectangleF(left - (ef2.Width/2f), (base.GraphArea.Bottom + 2), ef2.Width, Font.Height);
                    if(ef3.IntersectsWith(aboveRangeRect) || ef3.IntersectsWith(belowRangeRect))
                    {
                        left += num8;
                    }
                    else
                    {
                        graphics.DrawString(text, Font, brush2, ef3, format2);
                        left += num8;
                    }
                }
            }
        }

        private void DrawBarNames(Graphics graphics, float barArea, float barSpacing)
        {
            graphics.SetClip(base.ClientRectangle);
            var format = new StringFormat
                             {
                                         Trimming = StringTrimming.Character,
                                         FormatFlags = StringFormatFlags.NoWrap,
                                         LineAlignment = StringAlignment.Center
                             };
            float x = 0f;
            if(barOrientation == Orientation.Vertical)
            {
                Brush brush = new SolidBrush(XAxisColor);
                format.Alignment = StringAlignment.Center;
                x = base.GraphArea.Left + (barSpacing/2f);
                for(int i = 0; i < barCount; i++)
                {
                    string name = barCollection[i].Name;
                    var layoutRectangle = new RectangleF(x, base.GraphArea.Bottom, barArea, Font.Height*2f);
                    graphics.DrawString(name, Font, brush, layoutRectangle, format);
                    x += barArea;
                }
                brush.Dispose();
            }
            else
            {
                Brush brush2 = new SolidBrush(YAxisColor);
                format.Alignment = StringAlignment.Far;
                float num3 = barArea - barSpacing;
                x = ((base.GraphArea.Bottom - barSpacing) - (num3/2f)) - (base.FontHeight/2);
                for(int j = 0; j < barCount; j++)
                {
                    string s = barCollection[j].Name;
                    var ef2 = new RectangleF(base.ClientRectangle.Left, x, (GraphMarginLeft - (base.FontHeight/2)),
                                             Font.Height);
                    graphics.DrawString(s, Font, brush2, ef2, format);
                    x -= barArea;
                }
                brush2.Dispose();
            }
        }

        private void DrawRangeLinesAndValues(Graphics graphics)
        {
            graphics.SetClip(base.ClientRectangle);
            if(ShowRangeLines)
            {
                if(barOrientation == Orientation.Vertical)
                {
                    float num = base.GraphArea.Height*((AboveRangeValue - minimumValue)/(maximumValue - minimumValue));
                    float num2 = base.GraphArea.Height*((BelowRangeValue - minimumValue)/(maximumValue - minimumValue));
                    var pen = new Pen(Color.Black)
                                  {
                                              DashStyle = DashStyle.Dash
                                  };
                    graphics.DrawLine(pen, base.GraphArea.Left, base.GraphArea.Bottom - num, base.GraphArea.Right,
                                      base.GraphArea.Bottom - num);
                    graphics.DrawLine(pen, base.GraphArea.Left, base.GraphArea.Bottom - num2, base.GraphArea.Right,
                                      base.GraphArea.Bottom - num2);
                }
                else
                {
                    float num3 = base.GraphArea.Width*((AboveRangeValue - minimumValue)/(maximumValue - minimumValue));
                    float num4 = base.GraphArea.Width*((BelowRangeValue - minimumValue)/(maximumValue - minimumValue));
                    var pen2 = new Pen(Color.Black)
                                   {
                                               DashStyle = DashStyle.Dash
                                   };
                    graphics.DrawLine(pen2, base.GraphArea.Left + num3, base.GraphArea.Top, base.GraphArea.Left + num3,
                                      base.GraphArea.Bottom);
                    graphics.DrawLine(pen2, base.GraphArea.Left + num4, base.GraphArea.Top, base.GraphArea.Left + num4,
                                      base.GraphArea.Bottom);
                }
            }
            if(ShowRangeValues)
            {
                var format = new StringFormat();
                Brush brush = new SolidBrush(ForeColor);
                if(barOrientation == Orientation.Vertical)
                {
                    float num5 = base.GraphArea.Height*((AboveRangeValue - minimumValue)/(maximumValue - minimumValue));
                    float num6 = base.GraphArea.Height*((BelowRangeValue - minimumValue)/(maximumValue - minimumValue));
                    aboveRangeRect = new RectangleF(base.ClientRectangle.Left,
                                                    (base.GraphArea.Bottom - num5) - (Font.Height/2),
                                                    (GraphMarginLeft - (Font.Height/2)), Font.Height);
                    belowRangeRect = new RectangleF(base.ClientRectangle.Left,
                                                    (base.GraphArea.Bottom - num6) - (Font.Height/2),
                                                    (GraphMarginLeft - (Font.Height/2)), Font.Height);
                    format.Trimming = StringTrimming.Character;
                    format.FormatFlags = StringFormatFlags.NoWrap;
                    format.Alignment = StringAlignment.Far;
                    format.LineAlignment = StringAlignment.Center;
                    string s = "";
                    string str2 = "";
                    if(base.ValueFormat.Length != 0)
                    {
                        s = string.Format(CultureInfo.CurrentUICulture, base.ValueFormat, new object[] {aboveRangeValue});
                        str2 = string.Format(CultureInfo.CurrentUICulture, base.ValueFormat,
                                             new object[] {belowRangeValue});
                    }
                    else
                    {
                        s = aboveRangeValue.ToString(CultureInfo.CurrentUICulture);
                        str2 = belowRangeValue.ToString(CultureInfo.CurrentUICulture);
                    }
                    graphics.DrawString(s, Font, brush, aboveRangeRect, format);
                    graphics.DrawString(str2, Font, brush, belowRangeRect, format);
                }
                else
                {
                    float num7 = base.GraphArea.Width*((AboveRangeValue - minimumValue)/(maximumValue - minimumValue));
                    float num8 = base.GraphArea.Width*((BelowRangeValue - minimumValue)/(maximumValue - minimumValue));
                    string text = "";
                    string str4 = "";
                    if(base.ValueFormat.Length != 0)
                    {
                        text = string.Format(CultureInfo.CurrentUICulture, base.ValueFormat,
                                             new object[] {aboveRangeValue});
                        str4 = string.Format(CultureInfo.CurrentUICulture, base.ValueFormat,
                                             new object[] {belowRangeValue});
                    }
                    else
                    {
                        text = aboveRangeValue.ToString(CultureInfo.CurrentUICulture);
                        str4 = belowRangeValue.ToString(CultureInfo.CurrentUICulture);
                    }
                    SizeF ef = graphics.MeasureString(text, Font);
                    SizeF ef2 = graphics.MeasureString(str4, Font);
                    aboveRangeRect = new RectangleF((base.GraphArea.Left + num7) - (ef.Width/2f),
                                                    (base.GraphArea.Bottom + 2), ef.Width, Font.Height);
                    belowRangeRect = new RectangleF((base.GraphArea.Left + num8) - (ef2.Width/2f),
                                                    (base.GraphArea.Bottom + 2), ef2.Width, Font.Height);
                    format.Alignment = StringAlignment.Far;
                    format.LineAlignment = StringAlignment.Center;
                    graphics.DrawString(text, Font, brush, aboveRangeRect, format);
                    graphics.DrawString(str4, Font, brush, belowRangeRect, format);
                }
            }
        }

        private void InitializeComponent()
        {
            base.Name = "StackedBarGraph";
            base.Size = new Size(0x158, 0x100);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if(base.Visible)
            {
                Graphics graphics = e.Graphics;
                Draw(graphics);
                base.OnPaint(e);
            }
        }

        public void UpdateDisplay() { base.RefreshDisplay(); }
    }
}
